package com.sunlands.zlcx.usercenter.controller;

import com.sunlands.zlcx.usercenter.common.annotation.AutoLog;
import com.sunlands.zlcx.usercenter.domain.LearnContentDO;
import com.sunlands.zlcx.usercenter.domain.UsersLearnTimingDO;
import com.sunlands.zlcx.usercenter.exception.GlobalExceptionHandler;
import com.sunlands.zlcx.usercenter.service.UsersLearnContentServiceImpl;
import com.sunlands.zlcx.usercenter.service.UsersLearnTimingService;
import com.sunlands.zlcx.usercenter.util.DateUtil;
import com.sunlands.zlcx.usercenter.util.Version;
import com.sunlands.zlcx.usercenter.vo.count.LearnTimeChartVO;
import com.sunlands.zlcx.usercenter.vo.count.LearnTimeCount;
import com.sunlands.zlcx.usercenter.vo.count.LearnTimeOverView;
import com.sunlands.zlcx.usercenter.vo.response.BusinessResult;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.util.Asserts;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.stream.Collectors;

import static java.util.Collections.EMPTY_MAP;

/**
 * @author shixiaopeng
 */
@SuppressWarnings("unused")
@Api(tags = "12、用户学习时间记录", position = 12)
@ApiResponses(value = {@ApiResponse(code = 400, response = GlobalExceptionHandler.class, message = "数据校验失败"), @ApiResponse(code = 500, response = GlobalExceptionHandler.class, message = "内部错误")})
@Slf4j
@RestController
@RequestMapping("/learn/time")
public class UsersLearnTimeController implements AuthSupport {
    /**
     * 设置一个早于该功能的时间作为缺省值
     */
    public static final Date startTime = DateUtil.strToDate("2020-08-01");
    @Autowired
    private UsersLearnTimingService usersLearnTimingService;
    @Autowired
    private UsersLearnContentServiceImpl usersLearnContentService;
    private LearnContentDO UNKNOWN_CONTENT = LearnContentDO.builder().content("其他").build();


    @AutoLog
    @ApiOperation(value = "学习内容列表-总览")
    @PreAuthorize("isAuthenticated()")
    @GetMapping("/overview")
    public BusinessResult<List<LearnTimeOverView>> countLearnTime() {
        Long userId = currentUserId();
        Date now = new Date();

        Calendar calendar = Calendar.getInstance();
        calendar.setTime(now);
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        Date zero = calendar.getTime();

        List<LearnTimeOverView> overViews = Arrays.asList(usersLearnTimingService.countByUserId(userId, startTime, now), usersLearnTimingService.countByUserId(userId, zero, now));
        return BusinessResult.createSuccessInstance(overViews);

    }

    @AutoLog
    @ApiOperation(value = "学习内容列表-饼图、柱状图")
    @PreAuthorize("isAuthenticated()")
    @GetMapping("/chart/reference/content")
    public BusinessResult<LearnTimeCount> countByContent(@RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) Date startTime,
                                                         @RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) Date endTime,
                                                         HttpServletRequest request) {

        Asserts.notNull(startTime, "startTime不能为空！");
        Asserts.notNull(endTime, "endTime不能为空！");
        Asserts.check(startTime.compareTo(endTime) <= 0, "startTime必须小于等于endTime！");
        endTime = DateUtil.getDayEndTime(endTime);

        Long userId = currentUserId();
        List<LearnTimeChartVO> learnTimePie = usersLearnTimingService.countByLearnContent(userId, startTime, endTime);

        if (!learnTimePie.isEmpty()) {
            List<LearnContentDO> contents = usersLearnContentService.findAllByUserId(userId);
            learnTimePie.forEach(pie -> {
                String content = contents.stream().filter(c -> c.getId().equals(pie.getContentId())).findFirst().orElse(UNKNOWN_CONTENT).getContent();
                pie.setX_coordinate(content);
            });
        }

        Map<String, List<LearnTimeChartVO>> temp = learnTimePie.stream().collect(Collectors.groupingBy(LearnTimeChartVO::getX_coordinate, Collectors.toList()));
        learnTimePie.clear();

        for (String key : temp.keySet()) {
            if (temp.get(key).size() > 1) {
                LearnTimeChartVO chartVO = LearnTimeChartVO.builder()
                        .x_coordinate(temp.get(key).get(0).getX_coordinate())
                        .contentId(temp.get(key).get(0).getContentId())
                        .studyMinutes(temp.get(key).stream().mapToLong(LearnTimeChartVO::getStudyMinutes).sum())
                        .build();
                learnTimePie.add(chartVO);
            } else {
                learnTimePie.addAll(temp.get(key));
            }
        }

        //排序
        learnTimePie = learnTimePie.stream().sorted(Comparator.comparing(LearnTimeChartVO::getStudyMinutes).reversed()).collect(Collectors.toList());
        String version = getVersion(request);
        //对低版本学习内容40条限制进行兼容，如果没有低版本可以去掉
        if (learnTimePie.size() > 40 && Version.compareVersion(version, "3.4.0") < 0) {
            learnTimePie = learnTimePie.subList(0, 40);
        }

        LearnTimeCount result = LearnTimeCount.builder()
                .total(learnTimePie.stream().mapToLong(LearnTimeChartVO::getStudyMinutes).sum())
                .list(learnTimePie)
                .build();

        return BusinessResult.createSuccessInstance(result);
    }


    @AutoLog
    @ApiOperation(value = "学习内容列表、折线图")
    @PreAuthorize("isAuthenticated()")
    @GetMapping("/chart/reference/date")
    public BusinessResult<LearnTimeCount> countByDate(@RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) Date startTime,
                                                      @RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) Date endTime) {

        Asserts.notNull(startTime, "startTime不能为空！");
        Asserts.notNull(endTime, "endTime不能为空！");
        Asserts.check(startTime.compareTo(endTime) <= 0, "startTime必须小于等于endTime！");
        endTime = DateUtil.getDayEndTime(endTime);

        List<LearnTimeChartVO> learnTimeChart;
        Set<String> resultDateSet = new HashSet<>();
        String endStringDate = DateUtil.dateToStr(endTime);
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(startTime);

        if (DateUtil.dateToStr(startTime).substring(0, 7).equals(endStringDate.substring(0, 7))) {

            while (DateUtil.dateToStr(calendar.getTime()).compareTo(endStringDate) <= 0) {
                resultDateSet.add(DateUtil.dateToStr(calendar.getTime()));
                calendar.set(Calendar.DAY_OF_MONTH, calendar.get(Calendar.DAY_OF_MONTH) + 1);
            }

            List<LearnTimeChartVO> temp = usersLearnTimingService.countByDay(currentUserId(), startTime, endTime);
            learnTimeChart = resultDateSet.stream().map(s ->
                    temp.stream()
                            .filter(chart -> chart.getX_coordinate().equals(s)).findFirst()
                            .orElse(emptyChart(s))
            ).collect(Collectors.toList());

        } else {
            //统计维度为月
            endStringDate = endStringDate.substring(0, 7);
            while (DateUtil.dateToStr(calendar.getTime()).substring(0, 7).compareTo(endStringDate) <= 0) {
                resultDateSet.add(DateUtil.dateToStr(calendar.getTime()).substring(0, 7));
                calendar.set(Calendar.MONTH, calendar.get(Calendar.MONTH) + 1);
            }
            List<LearnTimeChartVO> temp = usersLearnTimingService.countByMonth(currentUserId(), startTime, endTime);

            learnTimeChart = resultDateSet.stream().map(s -> temp.stream()
                    .filter(chart -> chart.getX_coordinate().equals(s)).findFirst()
                    .orElse(emptyChart(s)))
                    .collect(Collectors.toList());
        }

        learnTimeChart = learnTimeChart.stream().sorted(Comparator.comparing(LearnTimeChartVO::getX_coordinate)).collect(Collectors.toList());

        LearnTimeCount result = LearnTimeCount.builder()
                .total(learnTimeChart.stream().mapToLong(LearnTimeChartVO::getStudyMinutes).sum())
                .list(learnTimeChart)
                .build();

        return BusinessResult.createSuccessInstance(result);
    }

    private LearnTimeChartVO emptyChart(String x_coordinate) {
        return LearnTimeChartVO
                .builder()
                .studyMinutes(0L)
                .x_coordinate(x_coordinate)
                .build();
    }

    @AutoLog
    @ApiOperation(value = "学习内容列表-柱状图-本周")
    @PreAuthorize("isAuthenticated()")
    @GetMapping("/chart/reference/week")
    public BusinessResult<LearnTimeCount> countByWeek(HttpServletRequest request) {

        Date startTime = DateUtil.getTimesWeekMorning();
        Date endTime = new Date();
        if (endTime.compareTo(startTime) < 0) {
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(startTime);
            calendar.set(Calendar.DAY_OF_MONTH, calendar.get(Calendar.DAY_OF_MONTH) - 7);
            startTime = calendar.getTime();
        }

        //返回数据格式定义
        List<Map<String, Map<Object, Long>>> result = new ArrayList<>();
        Long userId = currentUserId();
        List<LearnContentDO> contents = usersLearnContentService.findAllByUserId(userId);

        String endStringDate = DateUtil.dateToStr(endTime);
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(startTime);
        List<String> week = Arrays.asList("星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日");
        week.forEach(w -> {
            Map<String, Map<Object, Long>> temp = new HashMap<>();
            temp.put(w, EMPTY_MAP);
            result.add(temp);
        });

        List<UsersLearnTimingDO> learnTime = usersLearnTimingService.listByDate(userId, startTime, endTime);
        learnTime = learnTime.stream().peek(t -> {
            LearnContentDO learnContentDO = contents.stream().filter(c -> t.getContentId().equals(c.getId())).findFirst().orElse(UNKNOWN_CONTENT);
            t.setLearnName(learnContentDO.getContent());
        }).collect(Collectors.toList());


        //对低版本学习内容40条限制进行兼容，如果没有低版本可以去掉
        learnTime = filter(request, learnTime);

        Map<String, List<UsersLearnTimingDO>> allLearnTiming = learnTime.stream().collect(Collectors.groupingBy(l -> DateUtil.getWeek(l.getEndTime())));
        allLearnTiming.keySet().forEach(key -> {
                    //转换学习计时内容
                    Map<Object, Long> map = allLearnTiming.get(key).stream().collect(
                            Collectors.groupingBy(UsersLearnTimingDO::getLearnName, Collectors.summingLong(UsersLearnTimingDO::getStudyMinutes)
                            ));
                    result.forEach(r -> {
                        if (r.containsKey(key)) {
                            r.put(key, map);
                        }
                    });
                }
        );

        Map<String, Long> items = learnTime.stream().collect(Collectors.groupingBy(UsersLearnTimingDO::getLearnName, Collectors.summingLong(UsersLearnTimingDO::getStudyMinutes)));

        LearnTimeCount learnTimeCount = LearnTimeCount.builder()
                .total(result.stream().map(map -> {
                    long temp = 0L;
                    for (Map<Object, Long> v : map.values()) {
                        for (Long p : v.values()) {
                            temp += p;
                        }
                    }
                    return temp;
                }).mapToLong(Long::longValue).sum())
                .subItem(items)
                .list(result)
                .build();

        return BusinessResult.createSuccessInstance(learnTimeCount);
    }

    private List<UsersLearnTimingDO> filter(HttpServletRequest request, List<UsersLearnTimingDO> learnTime) {
        String version = getVersion(request);
        if (Objects.nonNull(version) && Version.compareVersion(version, "3.4.0") < 0) {
            List<Content> temp = new ArrayList<>();
            learnTime.stream()
                    .filter(lt -> Objects.nonNull(lt.getStudyMinutes()))
                    .collect(Collectors.groupingBy(UsersLearnTimingDO::getContentId,
                            Collectors.summingInt(UsersLearnTimingDO::getStudyMinutes)))
                    .forEach((k, v) -> temp.add(new Content(k, v)));

            if (temp.size() > 40) {
                List<Content> collect = temp.stream().sorted(Comparator.comparing(Content::getMinute)).collect(Collectors.toList());
                List<Long> contentIds = collect.subList(collect.size() - 40, collect.size()).stream().map(Content::getContentId).collect(Collectors.toList());
                learnTime = learnTime.stream()
                        .filter(lt -> contentIds.contains(lt.getContentId()))
                        .collect(Collectors.toList());
            }
            log.info("数据过滤，过滤后的数据 learnTime size = {}", learnTime.size());
        }
        return learnTime;
    }

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    static class Content {
        private Long contentId;
        private Integer minute;
    }

}
